/*
 * Copyright (C) li xiang
 */
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <stdio.h>

#define FILE_RD_ONLY        "r"
#define FILE_WR_ONLY        "w"
#define FILE_WR_PLUS        "w+"
#define FILE_APPEND         "a+"
#define WOZAPORT 5004
#define WOZAIP "192.168.142.240"
#define FILE_BUF 1024*1024*3
#define PSIA_ERR        1
#define PSIA_DBG        0
#define TS_SIZE             188
#define TS_HEADER_SIZE      4
#define TS_HEADER_SIZE_AF   6
#define TS_HEADER_SIZE_PCR  12
#define MAX_PCR_NUM 2000
#define MAX_SEND_SLICE_NUM 5000
/*static void psia_log(int isDebug, const char* format, ...)
{
    if (!isDebug)
        return;

    FILE* console_fd = fopen(PLATFORM_CONSOLE, FILE_APPEND);

    va_list args;
    va_start(args, format);
    vfprintf(console_fd, format, args);
    fputc('\n', console_fd);
    va_end(args);
    fflush(console_fd);
    return;
}*/
#define PSIA_LOG(SWITCH,  format, argv ...) ngx_log_stderr(SWITCH,  "%s() [%04d]| " format, __FILE__, __LINE__, ##argv)
typedef struct
{
    int pcr_pos;
    int pcr_value_u;//μs
} ngx_http_ts_pcr_info;
typedef struct
{
    int pack_num;
    int usleep_time;//μs
} ngx_ts_send_slice;
/* Module config */
typedef struct
{
    ngx_str_t ed;
} ngx_http_ts_recive_loc_conf_t;
static char *ngx_http_ts_recive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static void *ngx_http_ts_recive_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_ts_recive_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child);

/* Directives */
static ngx_command_t ngx_http_ts_recive_commands[] = { { ngx_string("ts_recive_li"),
NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1, ngx_http_ts_recive,
NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_ts_recive_loc_conf_t, ed), NULL },
ngx_null_command };
/* Http context of the module */
static ngx_http_module_t ngx_http_ts_recive_module_ctx = { NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_ts_recive_create_loc_conf, /* create location configration */
ngx_http_ts_recive_merge_loc_conf /* merge location configration */
};
/* Module */
ngx_module_t ngx_http_ts_recive_module = {
NGX_MODULE_V1, &ngx_http_ts_recive_module_ctx, /* module context */
ngx_http_ts_recive_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING };
typedef enum {
         false, true        } bool;
/*static ngx_int_t
 ngx_http_dav_handler(ngx_http_request_t *r)
 {
 ngx_int_t                 rc;
 ngx_http_dav_loc_conf_t  *dlcf;

 dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);

 if (!(r->method & dlcf->methods)) {
 return NGX_DECLINED;
 }

 switch (r->method) {

 case NGX_HTTP_PUT:

 if (r->uri.data[r->uri.len - 1] == '/') {
 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
 "cannot PUT to a collection");
 return NGX_HTTP_CONFLICT;
 }

 r->request_body_in_file_only = 1;
 r->request_body_in_persistent_file = 1;
 r->request_body_in_clean_file = 1;
 r->request_body_file_group_access = 1;
 r->request_body_file_log_level = 0;

 rc = ngx_http_read_client_request_body(r, ngx_http_dav_put_handler);

 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
 return rc;
 }

 return NGX_DONE;

 case NGX_HTTP_DELETE:

 return ngx_http_dav_delete_handler(r);

 case NGX_HTTP_MKCOL:

 return ngx_http_dav_mkcol_handler(r, dlcf);

 case NGX_HTTP_COPY:

 return ngx_http_dav_copy_move_handler(r);

 case NGX_HTTP_MOVE:

 return ngx_http_dav_copy_move_handler(r);
 }

 return NGX_DECLINED;
 }*/
static inline uint64_t tsaf_get_pcr(const uint8_t *p_ts)
{
    return ((uint64_t) p_ts[6] << 25) | (p_ts[7] << 17) | (p_ts[8] << 9) | (p_ts[9] << 1) | ((p_ts[10] & 0x80) >> 7);
}
static inline bool ts_has_adaptation(const uint8_t *p_ts)
{
    return !!(p_ts[3] & 0x20);
}

static inline uint8_t ts_get_adaptation(const uint8_t *p_ts)
{
    return p_ts[4];
}
static inline bool tsaf_has_pcr(const uint8_t *p_ts)
{
    return !!(p_ts[5] & 0x10);
}
static inline bool ts_validate(const uint8_t *p_ts)
{
    return p_ts[0] == 0x47;
}
static void handle_pcr(u_char *p_ts,ngx_http_ts_pcr_info* ppcr)
{
    uint64_t i_pcr = tsaf_get_pcr(p_ts);
    uint64_t i_pcr_ext=((uint64_t) (p_ts[10]& 0x01) << 8) | (p_ts[11]);
        i_pcr = i_pcr * 300 + i_pcr_ext;
        ppcr->pcr_value_u=i_pcr/27;
        PSIA_LOG(PSIA_DBG,"pcr_pos=%d pcr_value_u=%d\n",ppcr->pcr_pos,ppcr->pcr_value_u);
        return;
}
//return slice num
static int calculate_send_slice(ngx_ts_send_slice* pslice, ngx_http_ts_pcr_info* ppcr, int pcr_num)
{
    int i = 0,slice_num=0;
    for (i = 0; i < pcr_num; i++)
    {
        if (1 == i)
        {
            if (ppcr[i].pcr_pos > 7)
            {
                int seven_num = ppcr[i].pcr_pos / 7;
                int tmp = 0;
                for (tmp = 0; tmp < seven_num; tmp++)
                {
                    pslice[tmp].pack_num = 7;
                }
                pslice[tmp].pack_num = ppcr[i].pcr_pos % 7;
                pslice[tmp].usleep_time = ppcr[i].pcr_value_u - ppcr[i - 1].pcr_value_u;
                slice_num += tmp;
            }
        }
        else if (1 < i)
        {
            if ((ppcr[i].pcr_pos - ppcr[i - 1].pcr_pos) > 7)
            {
                int seven_num = (ppcr[i].pcr_pos - ppcr[i - 1].pcr_pos) / 7;
                int tmp = 0;
                for (tmp = 0; tmp < seven_num; tmp++)
                {
                    pslice[tmp + slice_num].pack_num = 7;
                }
                pslice[tmp + slice_num].pack_num = (ppcr[i].pcr_pos - ppcr[i - 1].pcr_pos) % 7;
                pslice[tmp + slice_num].usleep_time = ppcr[i].pcr_value_u - ppcr[i - 1].pcr_value_u;
                PSIA_LOG(PSIA_DBG,"slice pack=%d,sleep=%d num=%d\n",pslice[tmp + slice_num].pack_num,pslice[tmp + slice_num].usleep_time,tmp + slice_num);
                slice_num += tmp+1;
            }
            else
            {
                pslice[slice_num].pack_num = ppcr[i].pcr_pos - ppcr[i - 1].pcr_pos;
                pslice[slice_num].usleep_time = ppcr[i].pcr_value_u - ppcr[i - 1].pcr_value_u;
                slice_num++;
            }
        }
    }
    return slice_num;
}
//return usleep_time_send;
static int calculate_send_usleep_time(int readlen,ngx_ts_send_slice* send_slice,int slice_num){
    long long int total_usleep=0;
    int i=0,usleep_time_send=0;
    for(i=0;i<slice_num;i++){
        total_usleep+=send_slice[i].usleep_time;
    }
    usleep_time_send=total_usleep/(readlen/(188*7));
    return usleep_time_send;
}
//return pcr num;
static int calculate_psrpk_num(u_char* tsbuf,int buflen,ngx_http_ts_pcr_info* ppcr){
    u_char *p_ts = tsbuf;
    int num=0,i=0;
    while (p_ts < tsbuf + buflen && ts_validate(p_ts)) {
        if (ts_has_adaptation(p_ts) && ts_get_adaptation(p_ts) &&
            tsaf_has_pcr(p_ts)){
//               printf("%s  %d num=%d\n",__FILE__,__LINE__,num++);
            ppcr[i].pcr_pos=num;
            handle_pcr(p_ts,&ppcr[i]);
            i++;
            if(i==MAX_PCR_NUM){
                PSIA_LOG(PSIA_DBG,"pcr over buffer!!!!!!!!!!!!!!!!!!!!!!!!!");
                break;
            }
        }
        num++;
        p_ts += TS_SIZE;
    }
    PSIA_LOG(PSIA_DBG,"num=%d\n",num);
    return i;
}
static int read_ts_to_buf(u_char* filename, u_char* buf)
{
    int ret = 0,off=0;
    FILE *fp=NULL;
//    extern int errno;
/*    char* tmp=strrchr((const char*)filename,'/');
    char newname[100]={0};
    snprintf(newname,sizeof(newname),"/home/share/nginx-1.9.2/install/client_body_temp%s",tmp);
    PSIA_LOG(PSIA_DBG,"newname=%s\n",newname);*/
//    int file_len=0;
//    fp = fopen((const char*)newname, FILE_RD_ONLY);
    fp = fopen((const char*)filename, FILE_RD_ONLY);
    if (fp == NULL)
    {
      //  PSIA_LOG(PSIA_DBG,"error info=%s",strerror(errno));
        PSIA_LOG(PSIA_DBG,);
        return -1;
    }

/*    fseek(fp, 0L, SEEK_END);       //定位文件指针到尾部
    file_len = ftell(fp);          //获得文件长度
    fseek(fp, 0L, SEEK_SET);*/
    while(!feof(fp))               //判断文件是否结束
    {
        ret = fread(buf+off, 1, FILE_BUF, fp);
        off=off+ret;
        if (ferror(fp))
        {
            PSIA_LOG(PSIA_DBG,);
            fflush(fp);
            fclose(fp);
            return -1;
        }
    }
    fflush(fp);
    fclose(fp);
    return off;
}
static void ngx_http_ts_post_handler(ngx_http_request_t *r)
{
    ngx_chain_t* cl;
    if (r->request_body == NULL)
    {
        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
        return;
    }

    cl = r->request_body->bufs;
    PSIA_LOG(PSIA_DBG, "cl= %p buf=%p file=%p filename=%s\n", cl, r->request_body->buf, r->request_body->temp_file, r->request_body->temp_file->file.name.data);
//    PSIA_LOG(PSIA_DBG, "cl->buf->pos= %p  start=%p filesta=%d end=%d file=%p \n", cl->buf->pos, cl->buf->start, cl->buf->file_pos,
//            cl->buf->file_last, cl->buf->file);
//    PSIA_LOG(PSIA_DBG, "buf->pos= %p  last=%p start=%d end=%d file=%p \n", bf->pos, bf->last, bf->start, bf->end, bf->file);
    if (r->request_body->temp_file->file.name.data)
    {
        ngx_log_stderr(0, "*********************************");
        u_char buf[FILE_BUF] = { 0 };
        ngx_http_ts_pcr_info ts_pcr[MAX_PCR_NUM];
        ngx_ts_send_slice send_slice[MAX_SEND_SLICE_NUM];
        memset(ts_pcr, 0, sizeof(ts_pcr));
        memset(send_slice, 0, sizeof(send_slice));
        int ret = 0, readlen = 0;
        ret = read_ts_to_buf(r->request_body->temp_file->file.name.data, buf);
        if (ret < 0)
        {
            PSIA_LOG(PSIA_DBG,);
            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
            return;
        }
        readlen = ret;
        if (readlen % 188 != 0)
        {
            PSIA_LOG(PSIA_DBG,);
            ngx_http_finalize_request(r, NGX_HTTP_SERVICE_UNAVAILABLE);
            return;
        }

        int pcr_total = calculate_psrpk_num(buf, readlen, ts_pcr);

        int slice_num = calculate_send_slice(send_slice, ts_pcr, pcr_total);
        int usleep_time_send=calculate_send_usleep_time(readlen,send_slice,slice_num);
        if(0==usleep_time_send){

        }
        int sock_fd = socket(PF_INET, SOCK_DGRAM, 0);
        if (sock_fd < 0)
        {
            PSIA_LOG(PSIA_DBG,);
            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
            close(sock_fd);
            return;
        }
        PSIA_LOG(PSIA_DBG,);
        struct sockaddr_in sock_addr;
        bzero(&sock_addr, sizeof(sock_addr));
        sock_addr.sin_family = AF_INET;
        sock_addr.sin_port = htons(WOZAPORT);
        sock_addr.sin_addr.s_addr = inet_addr(WOZAIP);
        if (pcr_total <= 1)
        {
            int tmpret = 0;
            tmpret = sendto(sock_fd, buf,readlen, 0, (struct sockaddr *) &sock_addr, sizeof(sock_addr));
            if (-1 == tmpret)
            {
                //   PSIA_LOG(PSIA_DBG,"error info=%s",strerror(errno));
                PSIA_LOG(PSIA_DBG,);
                break;
            }
            return ;
        }
        int tmp = 0, tmpret = 0;
        int i = 0;
        for (i = 1; i < slice_num; i++)
        {
            tmpret = sendto(sock_fd, buf + tmp, (send_slice[i].pack_num) * 188, 0, (struct sockaddr *) &sock_addr, sizeof(sock_addr));
            if (-1 == tmpret)
            {
                //   PSIA_LOG(PSIA_DBG,"error info=%s",strerror(errno));
                PSIA_LOG(PSIA_DBG,);
                break;
            }
            tmp += (send_slice[i].pack_num) * 188;
            if (0 != send_slice[i].usleep_time)
            {
                usleep(send_slice[i].usleep_time);
            }
        }
/*        while(!(tmp>readlen))
        {
//            PSIA_LOG(PSIA_DBG,"slice pack=%d,sleep=%d\n",send_slice[i].pack_num,send_slice[i].usleep_time);
            tmpret = sendto(sock_fd, buf + tmp, 7 * 188, 0, (struct sockaddr *) &sock_addr, sizeof(sock_addr));
            if (-1 == tmpret)
            {
                //   PSIA_LOG(PSIA_DBG,"error info=%s",strerror(errno));
                PSIA_LOG(PSIA_DBG,);
                break;
            }
            tmp += 7 * 188;
            if (0 != send_slice[i].usleep_time)
            {
                usleep(send_slice[i].usleep_time);
            }
            usleep(usleep_time_send);
            if((tmp+ 188 * 7)>readlen){
                tmpret = sendto(sock_fd, buf + tmp, readlen%(7*188), 0, (struct sockaddr *) &sock_addr, sizeof(sock_addr));
                break;
            }
        }*/

        PSIA_LOG(PSIA_DBG,);
        return;
    }
}
/* Handler function */
static ngx_int_t ngx_http_ts_recive_handler(ngx_http_request_t *r)
{
    ngx_int_t rc;
    ngx_buf_t *b;
    ngx_chain_t out;
    ngx_http_ts_recive_loc_conf_t *elcf;
    elcf = ngx_http_get_module_loc_conf(r, ngx_http_ts_recive_module);
    if (!(r->method & (NGX_HTTP_HEAD | NGX_HTTP_GET | NGX_HTTP_POST)))
    {
        return NGX_HTTP_NOT_ALLOWED;
    }
    switch (r->method)
    {
    case NGX_HTTP_POST:

        if (r->uri.data[r->uri.len - 1] == '/')
        {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "cannot PUT to a collection");
            return NGX_HTTP_CONFLICT;
        }

        r->request_body_in_file_only = 1;
        r->request_body_in_persistent_file = 1;
        r->request_body_in_clean_file = 1;
        r->request_body_file_group_access = 1;
        r->request_body_file_log_level = 0;

        rc = ngx_http_read_client_request_body(r, ngx_http_ts_post_handler);

        if (rc >= NGX_HTTP_SPECIAL_RESPONSE)
        {
            break;
        }

        break;

    case NGX_HTTP_DELETE:
    case NGX_HTTP_MKCOL:
    case NGX_HTTP_COPY:
    case NGX_HTTP_MOVE:
        break;
    }
    r->headers_out.content_type.len = sizeof("text/html") - 1;
    r->headers_out.content_type.data = (u_char *) "text/html";
    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = elcf->ed.len;
    if (r->method == NGX_HTTP_HEAD)
    {
        rc = ngx_http_send_header(r);
        if (rc != NGX_OK)
        {
            return rc;
        }
    }
    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
    if (b == NULL)
    {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate response buffer.");
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    out.buf = b;
    out.next = NULL;
    b->pos = elcf->ed.data;
    b->last = elcf->ed.data + (elcf->ed.len);
    b->memory = 1;
    b->last_buf = 1;
    rc = ngx_http_send_header(r);
    if (rc != NGX_OK)
    {
        return rc;
    }
    return ngx_http_output_filter(r, &out);
}
static char *
ngx_http_ts_recive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_core_loc_conf_t *clcf;
    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    clcf->handler = ngx_http_ts_recive_handler;
    ngx_conf_set_str_slot(cf, cmd, conf);
    return NGX_CONF_OK;
}
static void *
ngx_http_ts_recive_create_loc_conf(ngx_conf_t *cf)
{
    ngx_http_ts_recive_loc_conf_t *conf;
    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ts_recive_loc_conf_t));
    if (conf == NULL)
    {
        return NGX_CONF_ERROR ;
    }
    conf->ed.len = 0;
    conf->ed.data = NULL;
    return conf;
}
static char *
ngx_http_ts_recive_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_http_ts_recive_loc_conf_t *prev = parent;
    ngx_http_ts_recive_loc_conf_t *conf = child;
    ngx_conf_merge_str_value(conf->ed, prev->ed, "");
    return NGX_CONF_OK;
}
